#include "Board.h"

using namespace Chess;

Board::Board(void)
{
  for(int i = 0; i < LEN; ++i)
    for(int j = 0; j < LEN; ++j)
      m_board[i][j] = 0;
}


Board::~Board(void)
{
}

unsigned Board::Clamp(int x)
{
  unsigned result = x;

  if(x <= 0)
    result = 0;
  if(x >= LEN)
    result = LEN - 1;

  return result;
}

Field* Board::GetField(unsigned x, unsigned y)
{
  return m_board[x % LEN][y % LEN];
}

void Board::Refresh()
{
  return;  
}


void Board::SetField(Field* field, unsigned x, unsigned y)
{
  m_board[x % LEN][y % LEN] = field;  
}

void Board::Select(unsigned x, unsigned y)
{
  x = x % LEN;
  y = y % LEN;

  this->ChangeStatus(FieldStatusEnum::SELECTED, x, y);  
}

void Board::DeselectAll(Chess::FieldStatusEnum status)
{
  for(unsigned i = 0; i < LEN; ++i)
  {
    for(unsigned j = 0; j < LEN; ++j)
    {
      if(m_board[i][j] != 0 && m_board[i][j]->GetStatus() == status || status == Chess::FieldStatusEnum::NONE)
        m_board[i][j]->SetStatus(FieldStatusEnum::NONE);
    }
  }
}

void Board::ChangeStatus(FieldStatusEnum status, unsigned x, unsigned y)
{
  x = x % LEN;
  y = y % LEN;

  if(m_board[x][y] != 0)
    m_board[x][y]->SetStatus(status);
}

std::vector<XMFLOAT2> Board::GetReachableFields(unsigned x, unsigned y, Chess::ColorEnum color)
{
  std::vector<XMFLOAT2> result;

  Field* f = GetField(x, y);
  if(f->Occupied() && f->GetFigure()->GetColor() == color)
    result = GetReachableFields(x, y);

  return result;
}

std::vector<XMFLOAT2> Board::GetReachableFields(unsigned x, unsigned y)
{
  std::vector<XMFLOAT2> result;

  x = x % LEN;
  y = y % LEN;

  Chess::Field* field = GetField(x,y);
  if(field == NULL)
    return result;

  if(field->Occupied())
  {
    IFigure* figure = field->GetFigure();
    Chess::FigureTypeEnum type = figure->GetType();

    switch(type)
    {
    case Chess::FigureTypeEnum::PAWN:
      CalculatePawnPositions(x,y, figure->GetColor(), &result);
      break;
    case Chess::FigureTypeEnum::KING:
      CalculateKingPosistions(x,y, figure->GetColor(), &result);
      break;
    case Chess::FigureTypeEnum::BISHOP:
      CalculateBishopPosistions(x,y, figure->GetColor(), &result);
      break;
    case Chess::FigureTypeEnum::ROOK:
      CalculateRookPosistions(x,y, figure->GetColor(), &result);
      break;
    case Chess::FigureTypeEnum::KNIGHT:
      CalculateKnightPosistions(x, y, figure->GetColor(), &result);
      break;
    case Chess::FigureTypeEnum::QUEEN:
      CalculateQueenPosistions(x, y, figure->GetColor(), &result);
    default:
      break;
    }
  }


  return result;
}

void Board::CalculatePawnPositions(unsigned x, unsigned y, Chess::ColorEnum color, std::vector<XMFLOAT2>* data)
{
  int direction = color == Chess::ColorEnum::WHITE ? 1 : -1;

  Field* field = GetField(Clamp(x + direction), y);
  if(field != NULL && !field->Occupied())
    data->push_back(XMFLOAT2(Clamp(x + direction), y));

  field = GetField(Clamp(x + direction), Clamp(y + 1));
  if(field != NULL && field->Occupied() && field->GetFigure()->GetColor() != color)
    data->push_back(XMFLOAT2(x + direction, y + 1));

  field = GetField(Clamp(x + direction), Clamp(y - 1));
  if(field != NULL && field->Occupied() && field->GetFigure()->GetColor() != color)
    data->push_back(XMFLOAT2(x + direction, y - 1));  
}

void Board::CalculateKingPosistions(unsigned x, unsigned y, Chess::ColorEnum color, std::vector<XMFLOAT2>* vector)
{
  XMFLOAT2 pos[] = 
  {
    XMFLOAT2(x, Clamp( y - 1 )),
    XMFLOAT2(x, Clamp( y + 1 )),
    XMFLOAT2(Clamp( x - 1 ), Clamp( y - 1 )),
    XMFLOAT2(Clamp( x - 1 ), y ),
    XMFLOAT2(Clamp( x - 1 ), Clamp( y + 1 )),
    XMFLOAT2(Clamp( x + 1 ), Clamp( y - 1 )),
    XMFLOAT2(Clamp( x + 1 ), y ),
    XMFLOAT2(Clamp( x + 1 ), Clamp( y + 1 )),
  };


  int n = ARRAYSIZE(pos);

  for(int i = 0; i < n; ++i)
  {
    Field* f = GetField(pos[i].x, pos[i].y);
    if(f != NULL)
    {
      if(f->Occupied())
      {
        if(f->GetFigure()->GetColor() != color)
          vector->push_back(pos[i]);
      }
      else
      {
        vector->push_back(pos[i]);
      }
    }    
  }
}

void Board::CalculateRookPosistions(unsigned x, unsigned y, Chess::ColorEnum color, std::vector<XMFLOAT2>* data)
{
  int i = y;

  while(--i > 0)
  {
    Field* f = GetField(x,i);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(x,i));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(x, i));
        break;
      }
    }
  }

  i = y;
  while(++i < LEN)
  {
    Field* f = GetField(x,i);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(x,i));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(x, i));
        break;
      }
    }
  }

  i = x;

  while(--i > 0)
  {
    Field* f = GetField(i, y);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(i, y));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(i, x));
        break;
      }
    }
  }

  i = x;
  while(++i < LEN)
  {
    Field* f = GetField(i, y);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(i, y));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(i, x));
        break;
      }
    }
  }
}

void Board::CalculateBishopPosistions(unsigned x, unsigned y, Chess::ColorEnum color, std::vector<XMFLOAT2>* data)
{
  int tmpX, tmpY;

  tmpX = x;
  tmpY = y;

  while(++tmpX < LEN && ++tmpY < LEN)
  {
    Field* f = GetField(tmpX, tmpY);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(tmpX, tmpY));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(tmpX, tmpY));
        break;
      }
    }
  }

  tmpX = x;
  tmpY = y;

  while(++tmpX < LEN && --tmpY >= 0)
  {
    Field* f = GetField(tmpX, tmpY);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(tmpX, tmpY));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(tmpX, tmpY));
        break;
      }
    }
  }

  tmpX = x;
  tmpY = y;

  while(--tmpX >= 0 && --tmpY >= 0)
  {
    Field* f = GetField(tmpX, tmpY);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(tmpX, tmpY));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(tmpX, tmpY));
        break;
      }
    }
  }

  tmpX = x;
  tmpY = y;

  while(--tmpX >= 0 && ++tmpY < LEN)
  {
    Field* f = GetField(tmpX, tmpY);
    if(f != NULL)
    {
      if(!f->Occupied())
        data->push_back(XMFLOAT2(tmpX, tmpY));
      else
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(XMFLOAT2(tmpX, tmpY));
        break;
      }
    }
  }
}


void Board::CalculateQueenPosistions(unsigned x, unsigned y, Chess::ColorEnum color, std::vector<XMFLOAT2>* data)
{
  CalculateBishopPosistions(x, y, color, data);
  CalculateRookPosistions(x, y, color, data);
}

void Board::CalculateKnightPosistions(unsigned x, unsigned y, Chess::ColorEnum color, std::vector<XMFLOAT2>* data)
{
  std::vector<XMFLOAT2> points;

  if((int) x - 2 >= 0)
  {
    if((int) y - 1 >= 0)
      points.push_back(XMFLOAT2(x - 2, y - 1));

    if((int) y + 1 < LEN)
      points.push_back(XMFLOAT2(x - 2, y + 1));
  }
    
  if((int) x + 2 < LEN)
  {
    if((int) y - 1 >= 0)
      points.push_back(XMFLOAT2(x + 2, y - 1));

    if((int) y + 1 < LEN)
      points.push_back(XMFLOAT2(x + 2, y + 1));
  }

  if((int) x - 1 >= 0)
  {
    if((int) y - 2 >= 0)
      points.push_back(XMFLOAT2(x - 1, y - 2));

    if((int) y + 2 < LEN)
      points.push_back(XMFLOAT2(x - 1, y + 2));
  }

  if((int) x + 1 < LEN)
  {
    if((int) y - 2 >= 0)
      points.push_back(XMFLOAT2(x + 1, y - 2));

    if((int) y + 2 < LEN)
      points.push_back(XMFLOAT2(x + 1, y + 2));
  }

  for(int i = 0; i < points.size(); ++i)
  {
    Field* f = GetField(points[i].x, points[i].y);
    if(f != NULL)
    {
      if(f->Occupied())
      {
        if(f->GetFigure()->GetColor() != color)
          data->push_back(points[i]);
      }
      else
      {
        data->push_back(points[i]);
      }
    }
  }
}
